/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Scene } from "arkanalyzer";
import Logger, { LOG_MODULE_TYPE } from "arkanalyzer/lib/utils/logger";
import { SceneConfig } from "arkanalyzer";
import { fileRuleMapping } from "./FileRuleMapping";
import { RuleConfig } from "../../model/RuleConfig";
import { ProjectConfig } from "../../model/ProjectConfig";
import { Project2Check } from "../../model/Project2Check";
import { File2Check } from "../../model/File2Check";
import { DisableText } from "./Disable";
import { Message } from "../../model/Message";
import { FileUtils } from "./FileUtils";
import { ScopeHelper } from "./ScopeHelper";
import { RuleListUtil } from "./DefectsList";
import { FixKey, FixMode } from "../../model/Fix";
import { IssueReport, engine } from "../../model/Defects";

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'CheckEntry');

export class CheckEntry {
    public ruleConfig: RuleConfig;
    public projectConfig: ProjectConfig;
    public projectCheck: Project2Check;
    public fileChecks: File2Check[] = [];
    public scene: Scene;
    public message: Message;

    constructor() {
    }

    public addFileCheck(fileCheck: File2Check) {
        this.fileChecks.push(fileCheck);
    }

    public addProjectCheck(projectCheck: Project2Check) {
        this.projectCheck = projectCheck;
    }

    public setDisableText(fileDisableText: string, nextLineDisableText: string) {
        DisableText.FILE_DISABLE_TEXT = fileDisableText;
        DisableText.NEXT_LINE_DISABLE_TEXT = nextLineDisableText;
    }

    public setEngineName(engineName: string) {
        engine.engineName = engineName;
    }

    public async runAll() {
        // TODO: worker_threads改造
        let checkedIndex = 1;
        for (const fileCheck of this.fileChecks) {
            try {
                await fileCheck.run();
                // 进度条通知
                this.message?.progressNotify(checkedIndex / (this.fileChecks.length + 1), fileCheck.arkFile.getFilePath());
                checkedIndex++;
            } catch (error) {
                logger.error(`Error running file check for ${fileCheck.arkFile.getFilePath()}: ${(error as Error).message}`);
                continue;
            }
        }

        if (this.projectCheck) {
            try {
                await this.projectCheck.run();
                this.message?.progressNotify(checkedIndex / (this.fileChecks.length + 1), 'Project Check');
            } catch (error) {
                logger.error(`Error running project check: ${(error as Error).message}`);
            }
        }
    }

    /**
     * 按file和checker两个维度分类，并返回分类后的结果
     * 
     * @returns {Object} 包含按检查器和文件分类的问题的映射
     */
    public sortIssues() {
        const issuesMapByChecker: Map<string, IssueReport[]> = new Map();
        const issuesMapByFile: Map<string, IssueReport[]> = new Map();
        RuleListUtil.printDefects();
        for (const fileCheck of this.fileChecks) {
            if (!fileCheck.issues || fileCheck.issues.length === 0) {
                continue;
            }
            issuesMapByFile.set(fileCheck.arkFile.getFilePath(), fileCheck.issues);
            for (const issue of fileCheck.issues) {
                logger.debug(issue.defect.mergeKey);
                const checkerStorage = issuesMapByChecker.get(issue.defect.ruleId);
                if (checkerStorage) {
                    checkerStorage.push(issue);
                } else {
                    issuesMapByChecker.set(issue.defect.ruleId, [issue]);
                }
            }
        }

        for (const issue of this.projectCheck?.issues ?? []) {
            logger.debug(issue.defect.mergeKey);
            const checkerStorage = issuesMapByChecker.get(issue.defect.ruleId);
            if (checkerStorage) {
                checkerStorage.push(issue);
            } else {
                issuesMapByChecker.set(issue.defect.ruleId, [issue]);
            }

            const filePath = issue.defect.mergeKey.split('%')[0];
            const fileStorage = issuesMapByFile.get(filePath);
            if (fileStorage) {
                fileStorage.push(issue);
            } else {
                issuesMapByFile.set(filePath, [issue]);
            }
        }
        issuesMapByChecker.forEach((issues, checker) => {
            logger.info(issues.length + ' issues from checker - ' + checker);
        });
        return { issuesMapByChecker, issuesMapByFile };
    }

    public buildScope(): void {
        new ScopeHelper().buildScope(this.scene);
    }

    /**
     * 执行代码修复操作
     */
    public codeFix(): void {
        let fixedFileCount = 0;
        let fixKeys = this.getFixKeys();
        //File2Check规则修复
        for (const fileCheck of this.fileChecks) {
            let keys: string[] = [];
            for (let key of fixKeys) {
                if (key.filePath === fileCheck.arkFile.getFilePath()) {
                    keys.push(key.fixKey);
                }
            }
            const fixResult = fileCheck.codeFix(keys);
            if (fixResult) {
                fixedFileCount++;
            }
        }
        //ProjectCheck规则修复
        fixedFileCount += this.projectCheck.codeFix(fixKeys).length;
        //总修复文件数量
        logger.info(`Fix ${fixedFileCount} files.`);
    }

    /**
     * 根据项目配置选择修复模式，后期可考虑支持多种模式混合搭配
     * 
     * @returns FixMode
     */
    public getFixMode(): FixMode {
        if (this.projectConfig.fixSelected) {
            return FixMode.AST;
        } else if (this.projectConfig.fix) {
            return FixMode.ARKFILE
        } else {
            return FixMode.AI;
        }
    }

    private getFixKeys(): FixKey[] {
        //TODO 从checkpath路径下读取需要修复的fixkey，
        let fixKeys: FixKey[] = [];
        fixKeys.push({
            filePath: 'D:\\workspace\\demo\\FPDemo\\entry\\src\\main\\ets\\array-type\\array-type.ts',
            fixKey: '16%10%22%@performance/array-type-check'
        });
        fixKeys.push({
            filePath: 'D:\\workspace\\demo\\FPDemo\\entry\\src\\main\\ets\\array-type\\array-type.ts',
            fixKey: '17%10%30%@performance/array-type-check'
        });
        fixKeys.push({
            filePath: 'D:\\workspace\\demo\\FPDemo\\entry\\src\\main\\ets\\array-type\\array-type.ts',
            fixKey: '18%10%20%@performance/array-type-check'
        });
        fixKeys.push({
            filePath: 'D:\\workspace\\demo\\FPDemo\\entry\\src\\main\\ets\\array-type\\array-type.ts',
            fixKey: '19%10%22%@performance/array-type-check'
        });
        fixKeys.push({
            filePath: 'D:\\workspace\\demo\\FPDemo\\entry\\src\\main\\ets\\prefer-const\\prefer-const.ts',
            fixKey: '2%7%7%@performance/prefer-const-check'
        });
        return fixKeys;
    }
}

export async function checkEntryBuilder(checkEntry: CheckEntry, checkFileList: string[]): Promise<boolean> {
    // 1、文件过滤和文件级屏蔽处理
    checkFileList = await FileUtils.getFiltedFiles(checkFileList, checkEntry.ruleConfig);
    logger.info("File count: " + checkFileList.length);
    if (checkFileList.length === 0) {
        checkEntry.message?.progressNotify(1, 'No file to check.');
        return false;
    }

    // 2、scene按需构建、scope构建
    if (!buildScene(checkFileList, checkEntry)) {
        return false;
    }

    // 3、规则和文件映射构建
    if (!(await fileRuleMapping(checkFileList, checkEntry))) {
        return false;
    }
    return true;
}

/**
 * 获取需要检查的文件列表
 * @param {CheckEntry} checkEntry - 检查条目
 * @returns {string[]} - 需要检查的文件列表
 */
export function getCheckFileList(checkEntry: CheckEntry): string[] {
    let checkFileList: string[] = [];
    if (checkEntry.projectConfig.checkPath.length > 0) {
        // 解析指定的文件
        const selectedFileInfos = FileUtils.getSeletctedFileInfos(checkEntry.projectConfig.checkPath, ['.ts', '.ets', '.json5']);
        checkFileList = selectedFileInfos.map(info => info.filePath);
    } else {
        // 获取全部文件
        checkFileList = FileUtils.getAllFiles(checkEntry.projectConfig.projectPath, ['.ts', '.ets', '.json5']);
    }
    return checkFileList;
}

/**
 * 构建Scene
 * @param fileList - 文件列表
 * @param checkEntry - 检查条目
 * @returns {boolean} - 构建是否成功
 */
function buildScene(fileList: string[], checkEntry: CheckEntry): boolean {
    try {
        // 构建SceneConfig信息
        const sceneConfig = new SceneConfig();
        const projectName = checkEntry.projectConfig.projectName;
        const projectPath = checkEntry.projectConfig.projectPath;
        const sdkList = FileUtils.genSdks(checkEntry.projectConfig);
        sceneConfig.buildFromProjectFiles(projectName, projectPath, fileList, sdkList);
        logger.info("Build sceneConfig completed.");
        // 构建Scene信息
        checkEntry.scene = new Scene();
        checkEntry.scene.buildSceneFromFiles(sceneConfig);
        logger.info("Build scene completed.");
        checkEntry.scene.inferTypes();
        logger.info("Infer types completed.");
    } catch (error) {
        logger.error("Build scene or infer types error: ", error);
        return false;
    }
    // 构建Scope信息
    checkEntry.buildScope();
    logger.info("Build scope completed.");
    return true;
}
